package engine;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URL;
import java.net.Proxy.Type;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.sql.DataSource;



public class PingBean {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private static Set<PingUrl> ADDRESS_LIST = new TreeSet<PingUrl>();
	private static int TIMEOUT = 50;
	
	private static List<DataSource> DS_LIST = new ArrayList<DataSource>();

	// retrying with the proxy
	private static Proxy PROXY;

	public static void checkApp() throws IOException {
		if (ADDRESS_LIST != null) {
			for (PingUrl pu : ADDRESS_LIST) {
				testConnection(pu);
			}
		}
		if (DS_LIST != null) {
			for (DataSource ds : DS_LIST) {
				testConnection(ds);
			}
		}
	}
	
	private static void testConnection (PingUrl pu) throws IOException {
   	 	Socket socket = null;
        try {
        	// will use http connection for urls that need proxy
        	// will use regular socket for other urls
        	if (! pu.isUseProxy()) {
		        socket = new Socket();
	        	SocketAddress socketAdress = new InetSocketAddress(pu.getHost(), pu.getPort());
	        	socket.connect(socketAdress, TIMEOUT);		        		
        	} else {
    			checkHttpConnWithProxy(pu);		        		
            	System.out.println("Check OK for " + pu.toString());
        	}
        } catch (Exception e) {
        	try {
        		if (pu.getProtocol().startsWith("http")) {
		        	// retry with the proxy
        			checkHttpConnWithProxy(pu);
        			// will set flag if we succeeded
        			pu.setUseProxy(true);
        		} else {
        			// no support for non http proxy
        			throw e;
        		}
        	} catch (Exception e2) {		        		
		        	System.out.println("Exception for: "+ pu.toString());
		        	e.printStackTrace();
//        		throw new RuntimeException(pu.getHost()+":"+pu.getPort(), e);
			}
        } finally {
        	if (socket != null) {
	        	socket.close();		        		
        	}
        }
	}
	
	private static void testConnection (DataSource dataSource) throws IOException {
		Connection con;
		try {
			con = dataSource.getConnection();
			con.close();
			
		} catch (SQLException e) {
			throw new IOException();
		}
	}

	/***
	 * This will make the http connection with the proxy in case we 
	 * failed on regular socket connection.
	 * 
	 * @param pu
	 * @throws IOException
	 */
	private static void checkHttpConnWithProxy (PingUrl pu) throws IOException {
		URL url = new URL(pu.getProtocol() + "://" + pu.getHost() + ":" + pu.getPort());
		HttpURLConnection conn = (HttpURLConnection)url.openConnection(PROXY);
		conn.connect();
		if (conn.getResponseCode() != 200) {
			throw new RuntimeException("Non valid http response for :" + pu.getHost() +":" + pu.getPort());
		}
	}

	public void setDataSourceList(List<DataSource> dsList) {
		if (dsList == null){
			return;
		}
		for(DataSource ds: dsList){
			DS_LIST.add(ds);
		}
	}

	public void setAddressList(List<String> addressList) {
		for (String address : addressList) {
			if (address.startsWith("jdbc")){
				continue;
			}
			PingUrl pu = new PingUrl();
			// this will replace all jdbc:// and smtp:// etc with http
    		try {
    			if (address.indexOf("://") < 0) {
        			address = "http://" + address;    				
    			}
    			URI uri = new URI(address); 
    			// in case we got a protocol that could not be handled and didn't receive a host
    			if (uri.getHost() == null) {
            		int endProtocol = address.indexOf("://");
                	address = "http" + address.substring(endProtocol);            			
                	uri = new URI(address);
    			} 
        		pu.setHost(uri.getHost());  
        		pu.setPort(uri.getPort());            			
    			pu.setProtocol(uri.getScheme().toLowerCase());
    			// setting default ports
    			if (pu.getPort() < 0) {
    				if (pu.getProtocol().equals("http")) {
    					pu.setPort(80);
    				} else if (pu.getProtocol().equals("https")) {
    					pu.setPort(443);
    				}
    			}
    			
    			ADDRESS_LIST.add(pu);
    			System.out.println("Adding ping url to the list: "+ pu.toString());
    		} catch (Exception e) {
				System.err.println("Cannot initialize the test for: "+ address);
				e.printStackTrace();
    		}
		}
	}

	public void setTimeout(int timeout) {
		TIMEOUT = timeout;
	}
	
	private InetSocketAddress httpProxyAddress;
	
	public InetSocketAddress getHttpProxyAddress() {
		return httpProxyAddress;
	}

	public void setHttpProxyAddress(InetSocketAddress httpProxyAddress) {
		this.httpProxyAddress = httpProxyAddress;
		String proxyHostName = httpProxyAddress.getHostName();
		try {
			if (proxyHostName == null || "".equals(proxyHostName) || 
					proxyHostName.startsWith("no.proxy")){
				return;
			}
			// setting the generic proxy for amdocs
			PROXY = new Proxy(Type.HTTP, httpProxyAddress);
		} catch (Exception e) {
			System.err.println("Cannot initialize proxy: "+ httpProxyAddress.getHostName()+":"+ httpProxyAddress.getPort());
			e.printStackTrace();
		}
	}
	
	private static class PingUrl implements Comparable<PingUrl>{
		private String host;
		private int port = 0;
		private String protocol;
		private boolean useProxy = false;
		
		public String getHost() {
			return host;
		}
		public void setHost(String host) {
			this.host = host;
		}
		public int getPort() {
			return port;
		}
		public void setPort(int port) {
			this.port = port;
		}
		
		@Override
		public int compareTo(PingUrl o) {
			if (o.getHost().equals(this.getHost())) {
				if (o.getPort() == this.getPort()) {
					return 0;
				}
			}
			return 1;
		}
		public String getProtocol() {
			return protocol;
		}
		public void setProtocol(String protocol) {
			this.protocol = protocol;
		}
		public boolean isUseProxy() {
			return useProxy;
		}
		public void setUseProxy(boolean useProxy) {
			this.useProxy = useProxy;
		}
		@Override
		public String toString() {
			return "PingUrl [host=" + host + ", port=" + port + ", protocol="
					+ protocol + ", useProxy=" + useProxy + "]";
		}
	}
	
	public static void main(String[] args) {
		PingBean pb = new PingBean();
		pb.setHttpProxyAddress(new InetSocketAddress("genproxy.amdocs.com", 8080));

		List<String> list = new ArrayList<String>();
		list.add("smtp://mail.013net.net.il:25");
		//list.add("stl.corp.amdocs.com");
		//list.add("google.com");
		//list.add("smtp://tlv.corp.amdocs.com:25");
		pb.setAddressList(list);
		try {
			pb.checkApp();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
